package com.zmn.mcc.cas.server;

import com.zmn.common.utils.cookie.CookieUtil;
import com.zmn.mcc.cas.core.MccCookie;
import com.zmn.mcc.cas.core.ShiroCasHelper;
import com.zmn.mcc.cas.core.ZmnSessionTouchCallback;
import lombok.extern.slf4j.Slf4j;
import org.apache.shiro.SecurityUtils;
import org.apache.shiro.authc.AuthenticationException;
import org.apache.shiro.authc.AuthenticationInfo;
import org.apache.shiro.authc.AuthenticationListener;
import org.apache.shiro.authc.AuthenticationToken;
import org.apache.shiro.session.Session;
import org.apache.shiro.subject.PrincipalCollection;
import org.apache.shiro.subject.Subject;
import org.apache.shiro.web.util.WebUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.data.redis.core.RedisTemplate;

import javax.servlet.http.Cookie;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.net.MalformedURLException;
import java.net.URL;

/**
 * 监听登录与登出，维护在线员工列表
 *
 * @author quanjic
 * @version v1.0
 * @since 2018/11/23 15:42
 **/
@Slf4j
public class ZmnStaffActiveHandler implements AuthenticationListener, ZmnSessionTouchCallback {

    private Logger logger = LoggerFactory.getLogger(ZmnStaffActiveHandler.class);

    private RedisTemplate redisTemplate;

    @Override
    public void onSuccess(AuthenticationToken token, AuthenticationInfo info) {
        String session = null;
        try {
            session = SecurityUtils.getSubject().getSession(true).getId().toString();
            logger.trace("#login#add staff to online list, session:{}", session);
            redisTemplate.opsForZSet().add(CasServerConsts.ONLINE_STAFF_KEY, session, System.currentTimeMillis());
        } catch (Exception e) {
            logger.error("#login#add staff to online list, session:{}", session, e);
        }
    }

    @Override
    public void onFailure(AuthenticationToken token, AuthenticationException ae) {
    }

    @Override
    public void onLogout(PrincipalCollection principals) {
        String session = null;
        try {
            session = SecurityUtils.getSubject().getSession(false).getId().toString();
            logger.debug("#login#remove staff from online list, session:{}", session);
            redisTemplate.opsForZSet().remove(CasServerConsts.ONLINE_STAFF_KEY, session);
        } catch (Exception e) {
            logger.error("#login#remove staff from online list, session:{}", session, e);
        }
    }

    public RedisTemplate getRedisTemplate() {
        return redisTemplate;
    }

    public void setRedisTemplate(RedisTemplate redisTemplate) {
        this.redisTemplate = redisTemplate;
    }

    @Override
    public void callback(Session session) {
        if (!SecurityUtils.getSubject().isAuthenticated()) {
            return;
        }
        Subject subject = SecurityUtils.getSubject();
        if (!WebUtils.isHttp(subject)) {
            return;
        }
        HttpServletRequest request = (HttpServletRequest) WebUtils.getRequest(subject);
        HttpServletResponse response = (HttpServletResponse) WebUtils.getResponse(subject);
        MccCookie cookieTemplate = ShiroCasHelper.getInstance().getSessionIdCookieTemplate();
        Cookie clientCookie = CookieUtil.getCookie(request, cookieTemplate.getName());
        if (clientCookie != null) {
            cookieTemplate.removeFrom(request, response);
            cookieTemplate.setValue(clientCookie.getValue());

            // 如果是https，就添加secure属性和允许跨域标识（http添加这俩属性,会导致cookie被浏览器拒绝）
            StringBuffer requestURL = request.getRequestURL();
            try {
                URL url = new URL(requestURL.toString());
                String protocol = url.getProtocol();
                if ("https".equalsIgnoreCase(protocol)) {
                    cookieTemplate.setSecure(Boolean.TRUE);
                    cookieTemplate.setSameSite("None");
                }
            } catch (MalformedURLException e) {
                log.error(e.getMessage());
            }

            cookieTemplate.saveTo(request, response);
        }
        logger.trace("#session#touch update online list, session:{}", session);
        getRedisTemplate().opsForZSet().add(CasServerConsts.ONLINE_STAFF_KEY, session.getId(), session.getLastAccessTime().getTime());
    }
}
